package org.pada.client;

import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.pada.client.dto.Cmd;
import org.pada.client.dto.Path;
import org.pada.client.dto.Resp;
import org.pada.client.exception.SystemErrorCode;
import org.pada.client.exception.SystemException;
import org.pada.client.util.JsonUtil;

import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

/**
 * @author shen.peng
 * @since 2024/10/25
 */
@Slf4j
public class CmdBus {
    private static final OkHttpClient httpClient = new OkHttpClient.Builder().connectTimeout(500L, TimeUnit.MILLISECONDS).writeTimeout(5000L, TimeUnit.MILLISECONDS).readTimeout(5000L, TimeUnit.MILLISECONDS).build();

    private static final MediaType JSON = MediaType.get("application/json; charset=utf-8");

    private static final ConcurrentHashMap<Class<? extends Cmd>, Type> CMD_RSP_MAP = new ConcurrentHashMap<>();

    public static <R extends Resp> R invoke(Cmd<R> cmd, ServiceConfig serviceConfig) {
        String body = JsonUtil.toJSONString(cmd);
        String url = serviceConfig.getDomain() + Optional.ofNullable(cmd.getClass().getAnnotation(Path.class))
                .map(Path::path).orElse(cmd.getClass().getName());
        Type type = CMD_RSP_MAP.computeIfAbsent(cmd.getClass(), CmdBus::getRespType);
        Request request = new Request.Builder().url(url).post(RequestBody.create(body, JSON)).build();
        log.info("consumer url {} content {}", url, body);
        try (Response response = httpClient.newCall(request).execute()) {
            ResponseBody resp = response.body();
            String data = Objects.nonNull(resp) ? resp.string() : null;
            log.info("consumer url {} resp {}", url, data);
            return JsonUtil.parseObject(data, type);
        } catch (IOException e) {
            log.error("rpc call exception", e);
            throw new SystemException(SystemErrorCode.RPC_CALL_EXCEPTION);
        }
    }

    private static Type getRespType(Class<? extends Cmd> clz) {
        Optional<Type> first = Arrays.stream(clz.getGenericInterfaces()).findFirst();
        if (!first.isPresent()) {
            throw new SystemException(SystemErrorCode.UN_DEFINE_ERR);
        }
        Type type = Arrays.stream(((ParameterizedType) first.get()).getActualTypeArguments()).findFirst().orElse(null);
        if (Objects.isNull(type)) {
            throw new SystemException(SystemErrorCode.UN_DEFINE_ERR);
        }
        return type;
    }

}
